package com.zy.explore.web.constraints.validator;

import com.zy.explore.web.constraints.InEnum;
import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author Administrator
 */
public class InEnumValidator implements ConstraintValidator<InEnum, Object> {

    private static final String errorMessage = "枚举 [{value}] 允许的枚举值 [permits] 输入错误，" +
        "错误的枚举值 {invalid}，允许输入的值 {allValues}";
    private final Set<String> invalid = new HashSet<>();
    private Class<? extends Enum<?>> enumClass;
    private boolean required;
    private Set<String> permits;
    private Set<String> allValues;

    @Override
    @SuppressWarnings("rawtypes")
    public void initialize(InEnum annotation) {
        enumClass = annotation.value();
        required = annotation.required();

        Enum[] enumConstants = enumClass.getEnumConstants();
        allValues = Arrays.stream(enumConstants).map(Enum::name).collect(Collectors.toSet());

        permits = new HashSet<>(allValues);

        List<String> limit = Arrays.asList(annotation.permits());
        if (!limit.isEmpty()) {
            permits.retainAll(limit);
            invalid.addAll(limit);
            invalid.removeAll(allValues);
        }
    }

    @Override
    public boolean isValid(Object maybeEnum, ConstraintValidatorContext context) {
        context.unwrap(HibernateConstraintValidatorContext.class)
            .addMessageParameter("value", enumClass.getSimpleName())
            .addMessageParameter("permits", permits.toString())
            .addMessageParameter("invalid", invalid.toString())
            .addMessageParameter("allValues", allValues.toString());

        if (!invalid.isEmpty()) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate(errorMessage)
                .addConstraintViolation();

            return false;
        }

        if (maybeEnum == null) {
            return !required;
        }

        return permits.contains(maybeEnum.toString());
    }
}
